// std functions
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <math.h>

// boost functions
#include <boost/program_options.hpp>
#include <boost/algorithm/string/case_conv.hpp>

// my functions
#include <print_error.h>
#include <index_string.h>
#include <string_partial_match.h>
#include <seq_pairs.h>
#include <program_options_helper.h>
#include <stlhelper.h>
#define DEBUG_LEVEL 6
#include <debug_func.h>
#include <blast_results.h>
#include "get_arg.h"
using std::vector;
using std::string;
using std::cerr;
using std::cout;
using std::max;
using boost::algorithm::to_upper_copy;




// link with -lboost_filesystem-gcc -lboost_program_options-gcc
// program_options_helper.cpp
//-lboost_filesystem-gcc-d
//-L/usr/local/lib

using namespace program_options_helper;
namespace po = boost::program_options_lg;


//_______________________________________________________________________________________

//		process_command_line_options

//				from command line
//				Created [9/13/2001]

//_______________________________________________________________________________________
bool process_command_line_options(int argc, char* argv[], t_program_args& args)
{

	//
	// checked mandatory arguments
	//
	//po::options_description mandatory_args("Mandatory Arguments");
	//mandatory_args.add_options()







	string matrix_name;
	args.optional_args.add_options()
	("allvsall,a",			"All vs all comparisons.")
	("identifiersvsall,i",	po::value<string>(&args.file_identifiers)
								->set_name("FILE"),
								"List of sequence identifiers. These will be aligned against "
								"all other sequences.")
	
	("reciprocals,R",   	po::bool_switch(&args.reciprocals),
								"Perform allvsall comparisons both ways")
	("pairs_file,p", 		po::value<string>(&args.file_pairs)
								->set_name("FILE"),
								"Tab-delimited sequence identifiers pairs.")
	("regex,r", 			po::value<string>(&args.regex)
								->set_name("REGEX")
								->default_value("(\\S+)"),
								"Regular expression for extracting the sequence identifier. "
								"Defaults to first non-whitespace accession characters.")
	("matrix,m", 	po::value<string>(&matrix_name)
								->set_name("MATRIX")
								->default_value("blosum80"),
								"MATRIX = dna, blosum62 or blosum80.")
	("e_value,E", 			po::value<float>(&args.cutoff)
								->set_name("E-VALUE")
								->default_value(1e-5),
								"Excludes alignments with larger E-values.")
	("alignment_min_len,l", po::value<unsigned>(&args.lencutoff)
								->set_name("LENGTH")
								->default_value(0),
								"Minimum sequence LENGTH to include.")
	("db_size,d", 			po::value<double>(&args.db_size)
								->set_name("COUNT")
								->default_value(1000686685),
								"Sequence database size for calculating bitscore and "
								"Expect value. Defaults to latest size of NCBI nr "
								"database.")
	("seg,s",	    		po::bool_switch(&args.seg),
								"Run seg before align.")
	("self_self,S",    		po::bool_switch(&args.self_self),
								"Keep self-self comparisons.")
	("header,H",    		po::bool_switch(&args.results_header),
								"Add a column header line to the results.")
	("columns,C",			"Print list of columns headers (and exit).")
	("blast_err_log,b", 	po::value<string>(&args.blast_err_log_name)
								->set_name("PATH"),
								"For logging bl2seq failures.")
	("#comment,#",			po::value<string>(&args.comment_letters)
									->set_name("LETTERS")
									->default_value("#"),
								"Use one of these letters to begin a "
								"commented line.");


	args.use_results_summary();
	args.add_std_options();
	po::options_description all_args("");                              
	all_args.add(args.optional_args);
		//.add(mandatory_args)

	// parse command line
	po::variables_map vm;
	po::store(po::command_line_parser(argc, argv).options(all_args).run(), vm);
	po::notify(vm);

	if (vm.count("version"))
	{
		print_version(cerr, *argv, "1.3\n"
			"\tHistory: \n\n"
			"\tv 1.0\1"
			"Initial version\n"
			"\tv 1.1\1"
			"30-07-2005\1"
			"Add identifiervsall option\1"
			"To help handle split large datasets without specifying pairs tediously.\n"
			"\tv 1.2\1"
			"09-10-2005\1"
			"Add db_size option\1"
			"Size of sequence database, for calculating bitscore and E-value.\n"
			"\tv 1.3\1"
			"04-01-2006\1"
			"For Andreas: Hash start comment lines. Fix bugs in handling input. Upgrade to common code "
			"for sequence pair identifiers\n"
			);
		return false;
	}

	if (vm.count("help"))
	{
		string exe_description =
								"Calculates pairwise local alignments.\n"
								"Sequences are read from STDIN.\n"
								"Defaults to successive pairwise alignments if sequence "
								"identifiers (--pairs_file / --identifiersvsall) or "
								"--allvsall are not specified.\n"
								"The column names for the resulting output can be "
								"obtained using --columns.\n";

		print_usage(cerr, string(*argv), "[OPTIONS] < SEQUENCES.FASTA > ALIGNMENT.RESULTS",
							all_args, exe_description, 80);
		return false;
	}
	if (vm.count("columns"))
	{
		cout	<< seq_pairs					::column_names() << "\t"
				<< bcpp_align::t_align_results	::column_names() << "\n";
		return false;

	}
	//check_required_options(vm, mandatory_args);


	//
	// Decide which matrix to use
	//
	{
		vector<string> names;
		names.push_back("BLOSUM62");
		names.push_back("BLOSUM80");
		names.push_back("DNA");
		t_string_partial_matches matrix_options;
		for (unsigned i = 0; i < names.size(); ++i)
			matrix_options.add_string(names[i],	i);
		matrix_options.add_string("62",	0, t_string_partial_matches::eMATCH_FULL);
		matrix_options.add_string("80",	1, t_string_partial_matches::eMATCH_FULL);
		std::vector<unsigned> matching_results;
		matrix_options.find_matches(to_upper_copy(matrix_name), matching_results);
		if (matching_results.size() != 1)
			throw std::runtime_error("Please specify blosum matrices of either \"blosum62\" "
											" \"blosum80\" or \"DNA\".");
		args.matrix = names[matching_results[0]];
	}
	
	unsigned options_chosen = 0;
	if (vm.count("allvsall"))
	{
		args.align_mode = eALIGN_ALLVSALL;
		++options_chosen;
	}
	if (!args.file_pairs.empty())
	{
		args.align_mode	= eALIGN_SPECIFIED;
		++options_chosen;
	}
	if (!args.file_identifiers.empty())
	{
		args.align_mode	= eALIGN_IDENTIFIERS;
		++options_chosen;
	}

	if (options_chosen > 1)
		throw std::runtime_error("Please specify ONE and only one out of \n"
								 "--allvsall   OR\n"
								 "--pairs_file OR\n"
								 "--identifiersvsall!");

	if (args.reciprocals && 
		!(args.align_mode == eALIGN_IDENTIFIERS ||
		  args.align_mode == eALIGN_ALLVSALL))
		throw std::runtime_error("--reciprocals only makes sense for \n"
								 "--allvsall   OR\n"
								 "--identifiersvsall!");
	args.open_log_files();

	args.open_err_log();
	return true;
}




void t_program_args::open_log_files()
{
	debug_func_cerr( 8 );
	// make sure the error log file is valid
	if (blast_err_log_name.length())
	{
		f_blast_err_log.open(blast_err_log_name.c_str(), std::ios_base::out | std::ios_base::ate);
		if (!f_blast_err_log.good())
			throw std::runtime_error("Could not open the blast error log file (" +
										blast_err_log_name + ")");
		blast_log_to_file = true;
	}
}



